//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

#include <maya/MPxLocatorNode.h>
#include <maya/MString.h>
#include <maya/MDagPath.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>
#include <maya/MVector.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MColor.h>
#include <maya/M3dView.h>
#include <maya/MFnPlugin.h>
#include <maya/MDistance.h>
#include <maya/MMatrix.h>
#include <maya/MFnUnitAttribute.h>

// Viewport 2.0 includes
#include <maya/MDrawRegistry.h>
#include <maya/MPxDrawOverride.h>
#include <maya/MUserData.h>
#include <maya/MDrawContext.h>
#include <maya/MGlobal.h>
#include <maya/MSelectionList.h>

// Foot Data
//
static float sole[][3] = { {  0.00f, 0.0f, -0.70f },
						   {  0.04f, 0.0f, -0.69f },
						   {  0.09f, 0.0f, -0.65f },
						   {  0.13f, 0.0f, -0.61f },
						   {  0.16f, 0.0f, -0.54f },
						   {  0.17f, 0.0f, -0.46f },
						   {  0.17f, 0.0f, -0.35f },
						   {  0.16f, 0.0f, -0.25f },
						   {  0.15f, 0.0f, -0.14f },
						   {  0.13f, 0.0f,  0.00f },
						   {  0.00f, 0.0f,  0.00f },
						   { -0.13f, 0.0f,  0.00f },
						   { -0.15f, 0.0f, -0.14f },
						   { -0.16f, 0.0f, -0.25f },
						   { -0.17f, 0.0f, -0.35f },
						   { -0.17f, 0.0f, -0.46f },
						   { -0.16f, 0.0f, -0.54f },
						   { -0.13f, 0.0f, -0.61f },
						   { -0.09f, 0.0f, -0.65f },
						   { -0.04f, 0.0f, -0.69f },
						   { -0.00f, 0.0f, -0.70f } };
static float heel[][3] = { {  0.00f, 0.0f,  0.06f },
						   {  0.13f, 0.0f,  0.06f },
						   {  0.14f, 0.0f,  0.15f },
						   {  0.14f, 0.0f,  0.21f },
						   {  0.13f, 0.0f,  0.25f },
						   {  0.11f, 0.0f,  0.28f },
						   {  0.09f, 0.0f,  0.29f },
						   {  0.04f, 0.0f,  0.30f },
						   {  0.00f, 0.0f,  0.30f },
						   { -0.04f, 0.0f,  0.30f },
						   { -0.09f, 0.0f,  0.29f },
						   { -0.11f, 0.0f,  0.28f },
						   { -0.13f, 0.0f,  0.25f },
						   { -0.14f, 0.0f,  0.21f },
						   { -0.14f, 0.0f,  0.15f },
						   { -0.13f, 0.0f,  0.06f },
						   { -0.00f, 0.0f,  0.06f } };
static int heelCount = 17;
static int soleCount = 21;

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Node implementation with standard viewport draw
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

class footPrint : public MPxLocatorNode
{
public:
	footPrint();
	virtual ~footPrint();

    virtual MStatus   		compute( const MPlug& plug, MDataBlock& data );

	virtual void            draw( M3dView & view, const MDagPath & path,
								  M3dView::DisplayStyle style,
								  M3dView::DisplayStatus status );

	virtual bool            isBounded() const;
	virtual MBoundingBox    boundingBox() const;

	static  void *          creator();
	static  MStatus         initialize();

	static  MObject         size;         // The size of the foot

public:
	static	MTypeId		id;
	static	MString		drawDbClassification;
	static	MString		drawRegistrantId;
};

MObject footPrint::size;
MTypeId footPrint::id( 0x80007 );
MString	footPrint::drawDbClassification("drawdb/geometry/footPrint");
MString	footPrint::drawRegistrantId("FootprintNodePlugin");

footPrint::footPrint() {}
footPrint::~footPrint() {}

MStatus footPrint::compute( const MPlug& /*plug*/, MDataBlock& /*data*/ )
{
	return MS::kUnknownParameter;
}

void footPrint::draw( M3dView & view, const MDagPath & /*path*/,
							 M3dView::DisplayStyle style,
							 M3dView::DisplayStatus status )
{
	// Get the size
	//
	MObject thisNode = thisMObject();
	MPlug plug( thisNode, size );
	MDistance sizeVal;
	plug.getValue( sizeVal );

	float multiplier = (float) sizeVal.asCentimeters();

	view.beginGL();


	if ( ( style == M3dView::kFlatShaded ) ||
		 ( style == M3dView::kGouraudShaded ) )
	{
		// Push the color settings
		//
		glPushAttrib( GL_CURRENT_BIT );

		if ( status == M3dView::kActive ) {
			view.setDrawColor( 13, M3dView::kActiveColors );
		} else {
			view.setDrawColor( 13, M3dView::kDormantColors );
		}

		glBegin( GL_TRIANGLE_FAN );
	        int i;
			int last = soleCount - 1;
			for ( i = 0; i < last; ++i ) {
				glVertex3f( sole[i][0] * multiplier,
							sole[i][1] * multiplier,
							sole[i][2] * multiplier );
			}
		glEnd();
		glBegin( GL_TRIANGLE_FAN );
			last = heelCount - 1;
			for ( i = 0; i < last; ++i ) {
				glVertex3f( heel[i][0] * multiplier,
							heel[i][1] * multiplier,
							heel[i][2] * multiplier );
			}
		glEnd();

		glPopAttrib();
	}

	// Draw the outline of the foot
	//
	glBegin( GL_LINES );
	    int i;
	    int last = soleCount - 1;
	    for ( i = 0; i < last; ++i ) {
			glVertex3f( sole[i][0] * multiplier,
						sole[i][1] * multiplier,
						sole[i][2] * multiplier );
			glVertex3f( sole[i+1][0] * multiplier,
						sole[i+1][1] * multiplier,
						sole[i+1][2] * multiplier );
		}
		last = heelCount - 1;
	    for ( i = 0; i < last; ++i ) {
			glVertex3f( heel[i][0] * multiplier,
						heel[i][1] * multiplier,
						heel[i][2] * multiplier );
			glVertex3f( heel[i+1][0] * multiplier,
						heel[i+1][1] * multiplier,
						heel[i+1][2] * multiplier );
		}
	glEnd();


	view.endGL();
}

bool footPrint::isBounded() const
{
	return true;
}

MBoundingBox footPrint::boundingBox() const
{
	// Get the size
	//
	MObject thisNode = thisMObject();
	MPlug plug( thisNode, size );
	MDistance sizeVal;
	plug.getValue( sizeVal );

	double multiplier = sizeVal.asCentimeters();

	MPoint corner1( -0.17, 0.0, -0.7 );
	MPoint corner2( 0.17, 0.0, 0.3 );

	corner1 = corner1 * multiplier;
	corner2 = corner2 * multiplier;

	return MBoundingBox( corner1, corner2 );
}

void* footPrint::creator()
{
	return new footPrint();
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Viewport 2.0 override implementation
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

class FootPrintData : public MUserData
{
public:
	FootPrintData() : MUserData(false) {} // don't delete after draw
	virtual ~FootPrintData() {}

	float fMultiplier;
	bool fIsSelected;
};

class FootPrintDrawOverride : public MHWRender::MPxDrawOverride
{
public:
	static MHWRender::MPxDrawOverride* Creator(const MObject& obj)
	{
		return new FootPrintDrawOverride(obj);
	}

	virtual ~FootPrintDrawOverride();

	virtual MBoundingBox boundingBox(
		const MDagPath& objPath,
		const MDagPath& cameraPath) const;

	virtual MUserData* prepareForDraw(
		const MDagPath& objPath,
		const MDagPath& cameraPath,
		MUserData* oldData);

	static void draw(const MHWRender::MDrawContext& context, const MUserData* data);

private:
	FootPrintDrawOverride(const MObject& obj);

	float getMultiplier(const MDagPath& objPath) const;
	bool getSelectionStatus(const MDagPath& objPath) const;
};

FootPrintDrawOverride::FootPrintDrawOverride(const MObject& obj)
: MHWRender::MPxDrawOverride(obj, FootPrintDrawOverride::draw)
{
}

FootPrintDrawOverride::~FootPrintDrawOverride()
{
}

float FootPrintDrawOverride::getMultiplier(const MDagPath& objPath) const
{
	// Retrieve value of the size attribute from the node
	MStatus status;
	MObject footprintNode = objPath.node(&status);
	if (status)
	{
		MPlug plug(footprintNode, footPrint::size);
		if (!plug.isNull())
		{
			MDistance sizeVal;
			if (plug.getValue(sizeVal))
			{
				return (float)sizeVal.asCentimeters();
			}
		}
	}

	return 1.0f;
}

bool FootPrintDrawOverride::getSelectionStatus(const MDagPath& objPath) const
{
	// retrieve the selection status of the node
	MStatus status;
	MSelectionList selectedList;
	status = MGlobal::getActiveSelectionList(selectedList);
	if(!status)
		return false;

	MDagPath pathCopy = objPath;
	do
	{
		if(selectedList.hasItem(pathCopy))
			return true;

		status = pathCopy.pop();
	}while(status);

	return false;
}

MBoundingBox FootPrintDrawOverride::boundingBox(
	const MDagPath& objPath,
	const MDagPath& cameraPath) const
{
	MPoint corner1( -0.17, 0.0, -0.7 );
	MPoint corner2( 0.17, 0.0, 0.3 );

	float multiplier = getMultiplier(objPath);
	corner1 = corner1 * multiplier;
	corner2 = corner2 * multiplier;

	return MBoundingBox(corner1, corner2);
}

MUserData* FootPrintDrawOverride::prepareForDraw(
	const MDagPath& objPath,
	const MDagPath& cameraPath,
	MUserData* oldData)
{
	// Cache multiplier attribute value for draw callback
	FootPrintData* data = dynamic_cast<FootPrintData*>(oldData);
	if (!data)
	{
		// data did not exist or was incorrect type, create new
		data = new FootPrintData();
	}
	data->fMultiplier = getMultiplier(objPath);
	data->fIsSelected = getSelectionStatus(objPath);

	return data;
}

void FootPrintDrawOverride::draw(const MHWRender::MDrawContext& context, const MUserData* data)
{
	// get cached data
	float multiplier = 1.0f;
	bool isSelected = false;
	const FootPrintData* footData = dynamic_cast<const FootPrintData*>(data);
	if (footData)
	{
		multiplier = footData->fMultiplier;
		isSelected = footData->fIsSelected;
	}

	// set colour
	static const float colorData[] = {1.0f, 0.0f, 0.0f};
	static const float selectedColorData[] = {0.0f, 1.0f, 0.0f};
	if(isSelected)
		glColor3fv(selectedColorData);
	else
		glColor3fv(colorData);


	MStatus status;

	// set world matrix
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	MMatrix transform =
		context.getMatrix(MHWRender::MDrawContext::kWorldViewMtx, &status);
	if (status)
	{
		glLoadMatrixd(transform.matrix[0]);
	}

	// set projection matrix
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	MMatrix projection =
		context.getMatrix(MHWRender::MDrawContext::kProjectionMtx, &status);
	if (status)
	{
		glLoadMatrixd(projection.matrix[0]);
	}

	const int displayStyle = context.getDisplayStyle();

	if(displayStyle & MHWRender::MDrawContext::kGouraudShaded)
	{
		// draw filled
		glPushAttrib( GL_CURRENT_BIT );

		glBegin( GL_TRIANGLE_FAN );
			int i;
			int last = soleCount - 1;
			for ( i = 0; i < last; ++i ) {
				glVertex3f( sole[i][0] * multiplier,
							sole[i][1] * multiplier,
							sole[i][2] * multiplier );
			}
		glEnd();
		glBegin( GL_TRIANGLE_FAN );
			last = heelCount - 1;
			for ( i = 0; i < last; ++i ) {
				glVertex3f( heel[i][0] * multiplier,
							heel[i][1] * multiplier,
							heel[i][2] * multiplier );
			}
		glEnd();

		glPopAttrib();
	}

	if(displayStyle & MHWRender::MDrawContext::kWireFrame)
	{
		// draw wire
		glBegin( GL_LINES );
			int i;
			int last = soleCount - 1;
			for ( i = 0; i < last; ++i ) {
				glVertex3f( sole[i][0] * multiplier,
							sole[i][1] * multiplier,
							sole[i][2] * multiplier );
				glVertex3f( sole[i+1][0] * multiplier,
							sole[i+1][1] * multiplier,
							sole[i+1][2] * multiplier );
			}
			last = heelCount - 1;
			for ( i = 0; i < last; ++i ) {
				glVertex3f( heel[i][0] * multiplier,
							heel[i][1] * multiplier,
							heel[i][2] * multiplier );
				glVertex3f( heel[i+1][0] * multiplier,
							heel[i+1][1] * multiplier,
							heel[i+1][2] * multiplier );
			}
		glEnd();
	}

	glPopMatrix();

	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Plugin Registration
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

MStatus footPrint::initialize()
{
	MFnUnitAttribute unitFn;
	MStatus			 stat;

	size = unitFn.create( "size", "sz", MFnUnitAttribute::kDistance );
	unitFn.setDefault( 1.0 );

	stat = addAttribute( size );
	if (!stat) {
		stat.perror("addAttribute");
		return stat;
	}

	return MS::kSuccess;
}

MStatus initializePlugin( MObject obj )
{
	MStatus   status;
	MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");

	status = plugin.registerNode(
				"footPrint",
				footPrint::id,
				&footPrint::creator,
				&footPrint::initialize,
				MPxNode::kLocatorNode,
				&footPrint::drawDbClassification);
	if (!status) {
		status.perror("registerNode");
		return status;
	}

    status = MHWRender::MDrawRegistry::registerDrawOverrideCreator(
		footPrint::drawDbClassification,
		footPrint::drawRegistrantId,
		FootPrintDrawOverride::Creator);
	if (!status) {
		status.perror("registerDrawOverrideCreator");
		return status;
	}

	return status;
}

MStatus uninitializePlugin( MObject obj)
{
	MStatus   status;
	MFnPlugin plugin( obj );

	status = MHWRender::MDrawRegistry::deregisterDrawOverrideCreator(
		footPrint::drawDbClassification,
		footPrint::drawRegistrantId);
	if (!status) {
		status.perror("deregisterDrawOverrideCreator");
		return status;
	}

	status = plugin.deregisterNode( footPrint::id );
	if (!status) {
		status.perror("deregisterNode");
		return status;
	}

	return status;
}
